'''

    2025年6月25日15:49:25
    批改作业的小程序2.1
    在之前的基础上加入excel\ppt\py的批改
    不再依赖于chatgpt，使用本地的千问
    尝试让AI阅读word文档：QWEN2+QWENVL
    思路：让QWENVL把图片给理解了，描述下图片，然后把作业内容和图片描述一起传给QWNE2让他给评语
    遍历指定目录下的所有.docx文件和.doc文件，然后直接追加评语和等级
    修改参数：
        一个拥有资深授课经验的老教师,教授课程为区块链原理与应用  修改教授课程
        FILE_DIR 要批改的作业文件夹，批改目录下所有word
        LOG_FILE 日志文件
        SCORE_FILE 简要记录成绩的文件
        PROMPT 提示词
        READ_IMG 是否读图的标志
        READ_IMG_SJ 是否随机读图，用于限制图片太多的文件
'''
import json
import os
import re
import sys
from os.path import basename
from time import sleep

import docx
import requests
import win32com.client as wc
import random
import datetime
import shutil
import traceback

from PIL import ImageGrab
from win32com.client import constants
import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import Font, Color
from openpyxl.utils import get_column_letter
from pptx import Presentation

qwvl_url = 'http://192.168.7.51:19000/getDes'
qw_url = 'http://192.168.7.51:18000/qwen'


## 获得评语AI版
def getPingyuAI(num):
    pingyu_result = []
    return pingyu_result


def printAndLogs(info):
    print(info)
    # logs.append(info)
    ## 3.写入日志
    with open(LOG_FILE, 'a+') as f:
        # # 写入文本
        # log_info = "本次任务发起时间为:" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "\n"
        f.writelines(info + '\n')
        # 每个元素后面加上换行符
        # sents = [log + '\n' for log in logs]
        # # 写入
        # f.writelines(sents)


def saveScore(info):
    ## 4.记录成绩
    with open(SCORE_FILE, 'a+') as f:
        # # 写入文本
        # log_info = "本次任务发起时间为:" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "\n"
        f.writelines(info + "\n")
        # # 每个元素后面加上换行符
        # sents = [score + '\n' for score in score_list]
        # # 写入
        # f.writelines(sents)


from func_timeout import func_set_timeout


@func_set_timeout(10)  # 设置最大执行时间为10秒
def load_excel_file(filepath):
    try:
        wb = load_workbook(filepath, data_only=True)
        return wb
    except Exception as e:
        print(e)
        return ""


## 1 遍历指定目录下的所有.docx文件和.doc文件
# FILE_DIR = ("D:\批改作业\\2024-2025-2\区块链2301班智能合约实训作业")
# FILE_DIR = ("D:\批改作业\\2024-2025-2\区块链2302班智能合约实训（实交36，应交37）\区块链2302班智能合约实训")
# FILE_DIR = ("D:\批改作业\\2024-2025-2\课后作业")
FILE_DIR = ("E:\实训任务拆分")
LOG_FILE = FILE_DIR + "\\logs.txt"
SCORE_FILE = FILE_DIR + "\\score.txt"
PROMPT = (
    "你是一位经验丰富的授课老师，请根据作业内容，给出一个严格要求下的评语，要求评语长度在200个字左右并指出优点和不足。同时给出一个等级，在ABCD中选择，等级越高，评语相对来讲越优秀。在最后留下批改时间，这次批改时间是2025年6月23日"
    "。作业内容：")
READ_IMG = True
READ_IMG_SJ = True

## 待处理的文件列表
FILE_PRO_LIST = []
logs = []

## 1.0 备份
source_path = os.path.abspath(FILE_DIR)
target_path = os.path.abspath(FILE_DIR + "_自动备份")
if not os.path.exists(target_path):
    if not os.path.exists(target_path):
        # 如果目标路径不存在原文件夹的话就创建
        os.makedirs(target_path)
    if os.path.exists(source_path):
        # 如果目标路径存在原文件夹的话就先删除
        shutil.rmtree(target_path)
    shutil.copytree(source_path, target_path)
printAndLogs('【@完成备份@】')

## 1.1 开始遍历指定目录
printAndLogs("1.开始遍历目录，探测需要修改的文档。。。")
for root, dirs, files in os.walk(FILE_DIR):
    for file in files:
        ## 1.2 开始判断文件是否是word
        filename = os.path.join(root, file)
        ## 1.2.1 判断是否已批改
        if filename.find("-已批改") > 0:
            continue
        if (filename.endswith(".doc")):
            ## 1.3 如果是doc文件，另存为.docx文件，然后把doc删掉
            # print(filename)
            try:
                word = wc.Dispatch('word.Application')
                doc = word.Documents.Open(filename)
                doc.SaveAs(filename + "x", 12)
                file = file + "x"
                doc.Close()
                os.remove(filename)
            except:
                continue
            sleep(1)
            printAndLogs("  doc文件已转为docx【" + filename + "x】")
            filename += "x"

        if (filename.endswith(".xls")):
            ## 1.3 如果是xls文件，另存为.xlsx文件，然后把xls删掉
            # print(filename)
            try:
                # 读取 xls 文件
                df = pd.read_excel(filename, engine='xlrd')
                # 保存为 xlsx 文件
                df.to_excel(f'{filename.split(".xls")[0]}.xlsx', index=False)
                print("转换完成！")
                os.remove(filename)
            except:
                continue
            sleep(1)
            printAndLogs(f"  xls文件已转为xlsx【{filename.split('.xls')[0]}.xlsx】")
            filename += f'{filename.split(".xls")[0]}.xlsx'

        if (filename.endswith(".docx") or filename.endswith(".xlsx") or filename.endswith(".pptx") or filename.endswith(".py")):
            ## 1.3 加入待处理列表
            # print(filename)
            FILE_PRO_LIST.append(filename)
# print(FILE_PRO_LIST)
printAndLogs("  探测完成！需编写评语的作业有【" + str(len(FILE_PRO_LIST)) + "】份")
## 2 操作word文件进行追加评语
from docx import Document
from docx.shared import RGBColor, Pt
from openpyxl import load_workbook

printAndLogs("2.开始尝试批改作业。。。")

index = 0
lese = []
score_list = []
all_number = len(FILE_PRO_LIST)
first_flag = 1

word = wc.Dispatch('word.Application')
word.Visible = False

# 创建一个目录来保存图片
images_dir = 'extracted_images'
if not os.path.exists(images_dir):
    os.makedirs(images_dir)

for filepath in FILE_PRO_LIST:
    # sleep(5) # 避免开文件太快报错
    ## 有的同学不讲武德，doc文件硬改docx ，python-docx不能改
    if filepath.endswith(".docx"):
        try:
            print(filepath)
            doc = Document(filepath)
        except Exception as e:
            print(e)
            try:
                doc = Document(filepath)
                doc.SaveAs(filepath, 12)
            except Exception as ee:
                print(ee)
                continue
            printAndLogs("  有个地雷！doc文件已转为docx【C" + filepath + "】")
            printAndLogs("  进度" + str(index + 1) + "/" + str(all_number) + "给【" + filepath + "】添加评语失败！")
            print(traceback.format_exc())
            index += 1
            lese.append(filepath)
            continue

        # 获取文档末尾段落
        try:
            # 提取docx文本内容
            a = list()
            pattern = re.compile('rId\d+')
            isFirst = True
            for graph in doc.paragraphs:
                for run in graph.runs:
                    if run.text != '':
                        a.append(run.text)
                    else:
                        # aaa = run.element.xml
                        # print(run.element.xml)
                        if not READ_IMG:
                            continue
                        try:
                            contentID = pattern.search(run.element.xml).group(0)
                        except AttributeError as e:
                            print(e)
                            continue
                        try:
                            contentType = doc.part.related_parts[contentID].content_type
                        except KeyError as e:
                            print(e)
                            continue
                        if not contentType.startswith('image'):
                            continue
                        # 如果是学校Logo，跳过
                        if isFirst:
                            isFirst = False
                            continue
                        # 如果开启随机模式，则只识别3的倍数的图
                        READ_IMG_SJ += 1
                        if READ_IMG_SJ % 3 != 0:
                            continue
                        imgName = basename(doc.part.related_parts[contentID].partname)
                        image_path = images_dir + "\\" + imgName
                        with open(image_path, "wb") as f:
                            f.write(doc.part.related_parts[contentID].blob)
                            print(f'  图片已保存到: {image_path}')
                        # 解读图片
                        current_dir = os.getcwd()
                        # 将当前目录与图片目录结合
                        absolute_images = os.path.join(current_dir + "\\" + image_path)
                        with open(absolute_images, 'rb') as img:
                            # # 设置请求头
                            # headers = {'Content-Type': 'multipart/form-data'}
                            # 发送POST请求，'file'是服务器端接收文件的字段名
                            try:
                                # 这里没有传query参数，那边就要设定问题
                                response = requests.post(qwvl_url, files={'file': img})
                                res = json.loads(response.text).get('res')
                                print(res)
                            except Exception as e:
                                print(f"发生了一个错误：{e}：{response}")
                        a.append("此处是一张图片，图片内容为：" + res[0])
            # 获取评语
            nr = ' '.join(a)
            print(len(nr))
            if len(nr) > 7000:
                nr = nr[:7000]
            response = requests.get(qw_url, params={"query": PROMPT + nr})
            res = json.loads(response.text).get('answer')

            # 在段落末尾添加红色文字
            pingyu_nr = res
            p = doc.paragraphs[-1]
            p.add_run("    ")
            run = p.add_run(pingyu_nr)
            font = run.font
            font.color.rgb = RGBColor(255, 0, 0)  # 设置文字颜色为红色
            doc.save(filepath[:-5] + "-已批改" + filepath[-5:])
            os.remove(filepath)
            printAndLogs("  进度" + str(index + 1) + "/" + str(all_number) + "已给【" + filepath + "】添加评语")
            # printAndLogs("     评语为：" + pingyu_nr)
            # score_list.append(filepath+"  ：  "+pingyu_nr)
            saveScore(filepath + "  :  " + pingyu_nr)
            index += 1
        except:
            printAndLogs("  进度" + str(index + 1) + "/" + str(all_number) + "给【" + filepath + "】添加评语失败！")
            print(traceback.format_exc())
            index += 1
            lese.append(filepath)
            continue
    if filepath.endswith(".xlsx"):
        try:
            print(f"   正在处理：{filepath}")
            # 加载 Excel 文件
            # wb = load_workbook(filepath, data_only=True)
            # wb = load_workbook(filepath)

            wb = load_excel_file(filepath)

            if wb == "":
                print("错误:", 123)
            else:
                print("成功加载")

            msgs = []
            # 遍历每个工作表
            for sheet_name in wb.sheetnames:
                ws = wb[sheet_name]
                # print(f"\n👉 正在处理工作表: {sheet_name}")
                msgs.append(f"\n👉 正在处理工作表: {sheet_name}")
                # 遍历每一行每一列的单元格
                for row in ws.iter_rows():
                    for cell in row:
                        # 获取单元格内容和部分样式信息
                        font = cell.font
                        fill = cell.fill
                        value = cell.value

                        # print(f"单元格 {cell.coordinate} (值: {value})")
                        # print(f"  字体: {font.name}, 大小: {font.size}, 是否加粗: {font.bold}")
                        # print(
                        #     f"  背景颜色: {fill.start_color.index if fill.type == 'gradient' or fill.type == 'pattern' else None}")
                        msgs.append(f"单元格 {cell.coordinate} (值: {value})")
                        msgs.append(f"  字体: {font.name}, 大小: {font.size}, 是否加粗: {font.bold}")
                        # msgs.append(f"  背景颜色: {fill.start_color.index if fill.type == 'gradient' or fill.type == 'pattern' else None}")
                        msgs.append(
                            f"  背景颜色: {fill.start_color.index if fill.fill_type in ['gradient', 'pattern'] else None}")
            nr = "  ".join(msgs)
            if len(nr) > 7000:
                nr = nr[:7000]
            response = requests.get(qw_url, params={"query": PROMPT + nr})
            res = json.loads(response.text).get('answer')

            # 新增一个名为“评语”的 sheet
            sheet_name = "评语"
            if sheet_name in wb.sheetnames:
                # 如果已存在，先删除
                wb.remove(wb[sheet_name])
            wb.create_sheet(sheet_name)
            ws = wb[sheet_name]

            # 写入评语内容，并设置红色字体
            red_font = Font(color="FF0000", bold=True)  # 红色 + 加粗

            ws['A1'] = "评语内容如下："
            ws['A1'].font = red_font

            ws['A2'] = res
            ws['A2'].font = red_font

            # 自动调整列宽
            for col in ws.columns:
                max_length = 0
                column = col[0].column_letter  # 获取列字母（如 A, B）
                for cell in col:
                    try:
                        if len(str(cell.value)) > max_length:
                            max_length = len(str(cell.value))
                    except:
                        pass
                adjusted_width = (max_length + 2)
                ws.column_dimensions[column].width = adjusted_width

            # 生成新文件名
            file_dir = os.path.dirname(filepath)
            file_name = os.path.basename(filepath)
            new_file_name = filepath.split('.xls')[0] + "-已批改.xlsx"
            new_file_path = os.path.join(file_dir, new_file_name)
            # 保存为新文件
            wb.save(new_file_path)
            os.remove(filepath)
            printAndLogs("  进度" + str(index + 1) + "/" + str(all_number) + "已给【" + filepath + "】添加评语")
            # printAndLogs("     评语为：" + pingyu_nr)
            # score_list.append(filepath+"  ：  "+pingyu_nr)
            saveScore(filepath + "  :  " + res)
            index += 1
        except:
            printAndLogs("  进度" + str(index + 1) + "/" + str(all_number) + "给【" + filepath + "】添加评语失败！")
            print(traceback.format_exc())
            index += 1
            lese.append(filepath)
            continue

    if filepath.endswith(".pptx"):
        try:
            print(f"   正在处理：{filepath}")
            # 1. 打开已有的PPTX文件
            presentation = Presentation(filepath)

            # 2. 可选：遍历所有幻灯片，读取内容
            content = ""
            for i, slide in enumerate(presentation.slides):
                # print(f"\n幻灯片 {i + 1} 内容：")
                for shape in slide.shapes:
                    if hasattr(shape, "text"):
                        content += shape.text  # 输出文本内容 [[7]]

            # 获取一个可用的幻灯片布局（防止索引越界）
            try:
                slide_layout = presentation.slide_layouts[6]  # 使用编号6作为常用空白布局
            except IndexError:
                slide_layout = presentation.slide_layouts[0]  # 回退到第一个可用布局

            # 添加新的幻灯片到最后
            comment_slide = presentation.slides.add_slide(slide_layout)

            # 设置标题（如果该布局支持标题）
            if comment_slide.shapes.title:
                comment_slide.shapes.title.text = "评语页"

            # 添加文本框
            left = top = width = height = 1  # 单位：英寸
            textbox = comment_slide.shapes.add_textbox(left, top + 1, width * 5, height)
            text_frame = textbox.text_frame
            # text_frame.text = "这里是评语内容。"
            if len(content) > 7000:
                content = content[:7000]
            response = requests.get(qw_url, params={"query": PROMPT + content})
            res = json.loads(response.text).get('answer')
            text_frame.text = res  # 添加评语 [[1]]



            # 生成新文件名
            file_dir = os.path.dirname(filepath)
            file_name = os.path.basename(filepath)
            new_file_name = filepath.split('.pptx')[0] + "-已批改.pptx"
            new_file_path = os.path.join(file_dir, new_file_name)
            # 保存为新文件
            # 6. 保存修改后的PPTX文件
            presentation.save(new_file_name)
            os.remove(filepath)
            printAndLogs("  进度" + str(index + 1) + "/" + str(all_number) + "已给【" + filepath + "】添加评语")
            # printAndLogs("     评语为：" + pingyu_nr)
            # score_list.append(filepath+"  ：  "+pingyu_nr)
            saveScore(filepath + "  :  " + res)
            index += 1
        except:
            printAndLogs("  进度" + str(index + 1) + "/" + str(all_number) + "给【" + filepath + "】添加评语失败！")
            print(traceback.format_exc())
            index += 1
            lese.append(filepath)
            continue

    if filepath.endswith(".py"):
        try:
            print(f"   正在处理：{filepath}")

            content = ""
            with open(filepath, 'r', encoding='utf-8') as file:
                content = file.read()

            original_content = content

            if len(content) > 7000:
                content = content[:7000]
            response = requests.get(qw_url, params={"query": PROMPT + content})
            res = json.loads(response.text).get('answer')
            # 步骤2：添加新内容
            new_text = res
            updated_content = original_content + new_text



            # 生成新文件名
            file_dir = os.path.dirname(filepath)
            file_name = os.path.basename(filepath)
            new_file_name = filepath.split('.py')[0] + "-已批改.py"
            new_file_path = os.path.join(file_dir, new_file_name)
            # 保存为新文件
            output_file_path = new_file_name  # 如果想覆盖原文件，可以设为 file_path

            with open(output_file_path, 'w', encoding='utf-8') as file:
                file.write(updated_content)
            os.remove(filepath)
            printAndLogs("  进度" + str(index + 1) + "/" + str(all_number) + "已给【" + filepath + "】添加评语")
            # printAndLogs("     评语为：" + pingyu_nr)
            # score_list.append(filepath+"  ：  "+pingyu_nr)
            saveScore(filepath + "  :  " + res)
            index += 1
        except:
            printAndLogs("  进度" + str(index + 1) + "/" + str(all_number) + "给【" + filepath + "】添加评语失败！")
            print(traceback.format_exc())
            index += 1
            lese.append(filepath)
            continue
printAndLogs("批量添加评语完成，失败【" + str(len(lese)) + "】份！")
for item in lese:
    printAndLogs("  " + item)
